#include "wnjni.h"

jobject Java_edu_princeton_cogsci_wn_ascii_CSynset_lookup
( JNIEnv* env, jclass sc, jint pos, jint offset, jstring jword )
{
  if ( pos < NOUN || pos > SATELLITE )
  {
    env->ThrowNew(
      env->FindClass( "java/lang/IllegalArgumentException" ),
      "pos was out of range!"
    );
    return NULL;
  }
  if ( pos == SATELLITE )
    pos = ADJ;

  jclass wordNetExceptionClass = env->FindClass( WORD_NET_EXCEPTION_CLASS );
  if ( wninit( ) )
  {
    env->ThrowNew( wordNetExceptionClass, "wninit( ) failed!" );
    return NULL;
  }

  char* word = "";
  if ( jword )
    word = strdup( env->GetStringUTFChars( jword, NULL ) );
  SynsetPtr ss = read_synset( pos, offset, word );
  if ( !word[0] )
    free( word );

  if ( !ss )
  {
    env->ThrowNew( wordNetExceptionClass, "read_synset( ) returned null!" );
    return NULL;
  }

  // ss (synset struct) is a pointer to the C struct.
  // sc (synset class) is the class ID of CSynset.
  // so (synset object) is an instance of CSynset.

  // If the synset is a satellite, fill in the headword and headsense fields.

  if ( getsstype( ss->pos ) == SATELLITE )
  {
    for ( int i = 0; i < ss->ptrcount; i++)
    {
      if ( ss->ptrtyp[i] == SIMPTR )
      {
        SynsetPtr cursyn = read_synset( ss->ppos[i], ss->ptroff[i], "" );
        if ( !cursyn )
        {
          env->ThrowNew( wordNetExceptionClass, "read_synset( ) failed!" );
          return NULL;
        }
        ss->headword = ( char * )malloc( strlen( cursyn->words[0] ) + 1 );
        if ( ss->headword == NULL )
        {
          env->ThrowNew( wordNetExceptionClass, "malloc( ) failed!" );
          return NULL;
        }
        strcpy( ss->headword, cursyn->words[0] );
        ss->headsense = cursyn->lexid[0];
        free_synset( cursyn );
        break;
      }
    }
  }

  jobject so = env->AllocObject( sc );
  if ( !so )
    return NULL;

  // Set int fields.

  env->SetIntField(
    so, env->GetFieldID( sc, "hereiam", "I" ), ss->hereiam
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "sstype", "I" ), ss->sstype
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "fnum", "I" ), ss->fnum
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "wcount", "I" ), ss->wcount
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "whichword", "I" ), ss->whichword
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "ptrcount", "I" ), ss->ptrcount
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "fcount", "I" ), ss->fcount
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "headsense", "I" ), ( int )ss->headsense
  );
  env->SetIntField(
    so, env->GetFieldID( sc, "pos", "I" ), getsstype( ss->pos )
  );

  // Set String fields.

  env->SetObjectField(
    so,
    env->GetFieldID( sc, "defn", "Ljava/lang/String;" ),
    env->NewStringUTF( ss->defn )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "headword", "Ljava/lang/String;" ),
    env->NewStringUTF( ss->headword )
  );
  
  // Set int array fields.

  env->SetObjectField(
    so,
    env->GetFieldID( sc, "lexid", "[I" ),
    new_ia( env, ss->wcount, ss->lexid )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "wnsns", "[I" ),
    new_ia( env, ss->wcount, ss->wnsns )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "ptrtyp", "[I" ),
    new_ia( env, ss->ptrcount, ss->ptrtyp )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "ppos", "[I" ),
    new_ia( env, ss->ptrcount, ss->ppos )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "pto", "[I" ),
    new_ia( env, ss->ptrcount, ss->pto )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "pfrm", "[I" ),
    new_ia( env, ss->ptrcount, ss->pfrm )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "frmid", "[I" ),
    new_ia( env, ss->fcount, ss->frmid )
  );
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "frmto", "[I" ),
    new_ia( env, ss->fcount, ss->frmto )
  );

  int* ptroff = new int[ ss->ptrcount ];
  for ( int i = 0; i < ss->ptrcount; i++ )
    ptroff[i] = ( int )ss->ptroff[i];
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "ptroff", "[I" ),
    new_ia( env, ss->ptrcount, ptroff )
  );
  delete[] ptroff;

  // Set String array field.
  
  env->SetObjectField(
    so,
    env->GetFieldID( sc, "words", "[Ljava/lang/String;" ),
    new_sa( env, ss->wcount, ss->words )
  );
  
  return so;
}

jintArray new_ia( JNIEnv* env, int size, int* buffer )
{
  jintArray ia = env->NewIntArray( size );
  if ( !ia )
    return NULL;
  env->SetIntArrayRegion( ia, 0, size, buffer );
  return ia;
}

jobjectArray new_sa( JNIEnv* env, int size, char** buffer )
{
  jobjectArray sa = env->NewObjectArray(
    size, env->FindClass( "java/lang/String" ), NULL
  );
  if ( !sa )
    return NULL;
  for ( int i = 0; i < size; i++ )
    env->SetObjectArrayElement( sa, i, env->NewStringUTF( buffer[i] ) );
  return sa;
}
